using System;
using Microsoft.BizTalk.ExplorerOM;
using System.Management;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Generic;

namespace BizTalk.BuildGenerator.Tasks
{
    /// <summary>
    /// This is a helper class for working with the BtsCatalogExplorer
    /// </summary>
    public sealed class BtsCatalogExplorerHelper
    {
        /// <summary>
        /// Ctor
        /// </summary>
        private BtsCatalogExplorerHelper()
        {
        }

        /// <summary>
        /// thios will check for the existance of a  biztalk application
        /// </summary>
        /// <param name="explorer"></param>
        /// <param name="applicationName"></param>
        /// <returns></returns>
        public static bool ApplicationExists(BtsCatalogExplorer explorer, string applicationName)
        {
            if (explorer == null)
                throw new NullReferenceException("The explorer has not been provided");

            foreach (Application btsApp in explorer.Applications)
            {
                if (btsApp.Name == applicationName)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// This will create an application
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="newApplicationName"></param>
        /// <param name="description"></param>
        public static void CreateApplication(BtsCatalogExplorer catalog, string newApplicationName, string description)
        {
            if (catalog == null)
                throw new NullReferenceException("The catalog is null");

            Application newApplication = catalog.AddNewApplication();
            newApplication.Name = newApplicationName;
            newApplication.Description = description;
            catalog.SaveChanges();
            catalog.Refresh();
        }

        /// <summary>
        /// This will delete an application
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="applicationName"></param>
        public static void DeleteApplication(BtsCatalogExplorer catalog, string applicationName)
        {
            if (catalog == null)
                throw new NullReferenceException("The catalog is null");

            Application appToRemove = catalog.Applications[applicationName];
            catalog.RemoveApplication(appToRemove);
            catalog.SaveChanges();
            catalog.Refresh();
        }

        /// <summary>
        /// This will start an application
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="applicationName"></param>
        public static void StartApplication(BtsCatalogExplorer catalog, string applicationName)
        {
            if (catalog == null)
                throw new NullReferenceException("The catalog is null");

            try
            {
                catalog.Applications[applicationName].Start(ApplicationStartOption.StartAll);
                catalog.SaveChanges();
                catalog.Refresh();
            }
            catch (Exception ex)
            {
                AlternativeStart(catalog, applicationName);
            }
        }
        /// <summary>
        /// Tries to start the app one piece at a time as there was a problem doing it in bulk
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="applicationName"></param>
        private static void AlternativeStart(BtsCatalogExplorer catalog, string applicationName)
        {
            Trace.WriteLine("Attempting alternative method to start application");
            Application app = catalog.Applications[applicationName];
         
            foreach (SendPort sendPort in app.SendPorts)
            {
                Trace.WriteLine(string.Format("Starting send port: {0}", sendPort.Name));
                StartSendPort(sendPort.Name);
            }

            foreach (ReceivePort recievePort in app.ReceivePorts)
            {
                foreach (ReceiveLocation recieveLocation in recievePort.ReceiveLocations)
                {
                    Trace.WriteLine(string.Format("Starting recieve location: {0}", recieveLocation.Name));
                    EnableRecieveLocation(recieveLocation.Name, recievePort.Name);
                }
            }

            foreach (BtsOrchestration orchestration in app.Orchestrations)
            {
                Trace.WriteLine(string.Format("Starting orchestration: {0}", orchestration.FullName));
                EnlistOrchestration(orchestration.FullName, orchestration.BtsAssembly.DisplayName, orchestration.Host.Name);
                StartOrchestration(orchestration.FullName, orchestration.BtsAssembly.DisplayName);
            }            
        }
        /// <summary>
        /// Enables a recieve loation
        /// </summary>
        /// <param name="name"></param>
        /// <param name="portName"></param>
        private static void EnableRecieveLocation(string name, string portName)
        {
            const string ScopeFormat = "root\\MicrosoftBizTalkServer";
            const string ManagementPathFormat = "MSBTS_ReceiveLocation.Name='{0}',ReceivePortName='{1}'";
            Trace.WriteLine("Starting recieve location: " + name);

            string managementPath = string.Format(ManagementPathFormat, name, portName);
            ManagementObject classInstance = new ManagementObject(ScopeFormat, managementPath, null);

            ManagementBaseObject outParams = classInstance.InvokeMethod("Enable", null, null);
        }
        /// <summary>
        /// Starts a send port
        /// </summary>
        /// <param name="name"></param>
        private static void StartSendPort(string name)
        {
            const string ScopeFormat = "root\\MicrosoftBizTalkServer";
            const string ManagementPathFormat = "MSBTS_SendPort.Name='{0}'";
            Trace.WriteLine("Starting send port: " + name);

            string managementPath = string.Format(ManagementPathFormat, name);
            ManagementObject classInstance = new ManagementObject(ScopeFormat, managementPath, null);

            ManagementBaseObject outParams = classInstance.InvokeMethod("Start", null, null);
            Trace.WriteLine("ReturnValue: " + outParams["ReturnValue"]);
        }
        /// <summary>
        /// Starts the orchestration
        /// </summary>
        /// <param name="name"></param>
        /// <param name="assemblyFullName"></param>
        private static void StartOrchestration(string name, string assemblyFullName)
        {
            const string ScopeFormat = "root\\MicrosoftBizTalkServer";
            const string ManagementPathFormat = "SELECT * FROM MSBTS_Orchestration WHERE AssemblyName = '{0}' And Name = '{1}'";

            AssemblyName assemblyName = new AssemblyName(assemblyFullName);

            Trace.WriteLine("Starting - Orchestration Name: " + name);
            Trace.WriteLine("Orchestration Assembly Name: " + assemblyFullName);

            List<string> args = new List<string>();
            args.Add(assemblyName.Name);
            args.Add(name);
            args.Add(assemblyName.Version.ToString());
            string managementPath = string.Format(ManagementPathFormat, args.ToArray());
            Trace.WriteLine("Management Path: " + managementPath);

            string query = string.Format(ManagementPathFormat, args.ToArray());
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(ScopeFormat, query);

            foreach (ManagementObject orchestrationManagementObject in searcher.Get())
            {
                Trace.WriteLine("Found orchestration: " + (string)orchestrationManagementObject["Name"]);

                ManagementBaseObject inParams = orchestrationManagementObject.GetMethodParameters("Start");
                inParams["AutoEnableReceiveLocationFlag"] = 1;
                inParams["AutoResumeOrchestrationInstanceFlag"] = 1;
                inParams["AutoStartSendPortsFlag"] = 1;

                ManagementBaseObject outParams = orchestrationManagementObject.InvokeMethod("Start", inParams, null);
                Trace.WriteLine("ReturnValue: " + outParams["ReturnValue"]);
            }
        }
        /// <summary>
        /// Starts the orchestration
        /// </summary>
        /// <param name="name"></param>
        /// <param name="assemblyFullName"></param>
        /// <param name="hostName"></param>
        private static void EnlistOrchestration(string name, string assemblyFullName, string hostName)
        {
            const string ScopeFormat = "root\\MicrosoftBizTalkServer";
            const string ManagementPathFormat = "SELECT * FROM MSBTS_Orchestration WHERE AssemblyName = '{0}' And Name = '{1}'";

            AssemblyName assemblyName = new AssemblyName(assemblyFullName);

            Trace.WriteLine("Enlisting - Orchestration Name: " + name);
            Trace.WriteLine("Orchestration Assembly Name: " + assemblyFullName);

            List<string> args = new List<string>();
            args.Add(assemblyName.Name);
            args.Add(name);
            args.Add(assemblyName.Version.ToString());
            string managementPath = string.Format(ManagementPathFormat, args.ToArray());
            Trace.WriteLine("Management Path: " + managementPath);

            string query = string.Format(ManagementPathFormat, args.ToArray());
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(ScopeFormat, query);

            foreach (ManagementObject orchestrationManagementObject in searcher.Get())
            {
                Trace.WriteLine("Found orchestration: " + (string)orchestrationManagementObject["Name"]);

                ManagementBaseObject inParams = orchestrationManagementObject.GetMethodParameters("Enlist");
                inParams["HostName"] = hostName;
                ManagementBaseObject outParams = orchestrationManagementObject.InvokeMethod("Enlist", inParams, null);
            }
        }
        

        /// <summary>
        /// This will stop an application
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="applicationName"></param>
        public static void StopApplication(BtsCatalogExplorer catalog, string applicationName)
        {
            if (catalog == null)
                throw new NullReferenceException("The catalog is null");

            Application app = catalog.Applications[applicationName];
            app.Stop(ApplicationStopOption.StopAll);
            catalog.SaveChanges();
            catalog.Refresh();
        }
    }
}